import re
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk


class BibliographyManager:
    def __init__(self):
        self.bib_id_list = []
        self.bibitem_list = {}  # Dictionary to store bibitem_id -> bibitem_content
        self.reference_file_content = ""
        
    def extract_cite_ids(self, file_path):
        """Extract citation IDs from a .tex file."""
        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
                
            # Find all \cite{...} patterns
            cite_patterns = re.findall(r'\\cite\{([^}]+)\}', content)
            
            # Process each citation (some may contain multiple IDs separated by commas)
            ids = []
            for pattern in cite_patterns:
                ids.extend([id.strip() for id in pattern.split(',')])
                
            return ids
        except Exception as e:
            print(f"Error extracting citations from {file_path}: {e}")
            return []
    
    def read_tex_files_in_order(self, directory):
        """Read .tex files in specified order and extract citation IDs."""
        self.bib_id_list = []
        file_names = ["1.tex", "2.tex", "3.tex", "4.tex"]
        
        for file_name in file_names:
            file_path = os.path.join(directory, file_name)
            if os.path.exists(file_path):
                ids = self.extract_cite_ids(file_path)
                for id in ids:
                    if id not in self.bib_id_list:
                        self.bib_id_list.append(id)
            else:
                print(f"Warning: {file_path} not found, skipping.")
        
        return self.bib_id_list
    
    def extract_bibitems(self, reference_file_path):
        """Extract bibitem entries from the reference file."""
        try:
            with open(reference_file_path, 'r', encoding='utf-8') as file:
                self.reference_file_content = file.read()
                
            # Extract all bibitem blocks
            bibitem_pattern = r'\\bibitem\{([^}]+)\}((?:.|\n)*?)(?=\\bibitem\{|\\end\{thebibliography\})'
            matches = re.findall(bibitem_pattern, self.reference_file_content)
            
            self.bibitem_list = {}
            for match in matches:
                bibitem_id = match[0].strip()
                bibitem_content = match[1].strip()
                self.bibitem_list[bibitem_id] = bibitem_content
                
            return self.bibitem_list
        except Exception as e:
            print(f"Error extracting bibitems from {reference_file_path}: {e}")
            return {}
    
    def check_missing_references(self):
        """Check if all citation IDs have corresponding bibitem entries."""
        missing_ids = []
        for id in self.bib_id_list:
            if id not in self.bibitem_list:
                missing_ids.append(id)
        
        return missing_ids
    
    def reorder_and_save_bibitems(self, output_path):
        """Reorder bibitems according to bib_id_list and save to the output file."""
        try:
            # Extract the header and footer of the reference file
            header_match = re.search(r'(.*?\\begin\{thebibliography\}\{200\}(?:.|\n)*?)\\bibitem\{', 
                                    self.reference_file_content, re.DOTALL)
            footer_match = re.search(r'(\\end\{thebibliography\}.*)', 
                                    self.reference_file_content, re.DOTALL)
            
            if not header_match or not footer_match:
                raise Exception("Could not identify the bibliography structure in the reference file.")
            
            header = header_match.group(1)
            footer = footer_match.group(1)
            
            # Construct the new content with ordered bibitems
            new_content = header
            
            for bib_id in self.bib_id_list:
                if bib_id in self.bibitem_list:
                    new_content += f"\n\t\\bibitem{{{bib_id}}}\n\t{self.bibitem_list[bib_id]}\n"
            
            new_content += footer
            
            # Write to the output file
            with open(output_path, 'w', encoding='utf-8') as file:
                file.write(new_content)
                
            return True
        except Exception as e:
            print(f"Error reordering and saving bibitems: {e}")
            return False


class BibManagerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("LaTeX Bibliography Manager")
        self.root.geometry("800x600")
        
        self.bib_manager = BibliographyManager()
        
        self.setup_ui()
    
    def setup_ui(self):
        # Create a notebook (tabbed interface)
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # Tab 1: File Selection
        self.tab1 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab1, text="1. 选择文件")
        
        # Tab 2: Results and Actions
        self.tab2 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab2, text="2. 结果和操作")
        
        # Setup Tab 1
        self.setup_tab1()
        
        # Setup Tab 2
        self.setup_tab2()
    
    def setup_tab1(self):
        # Directory selection
        ttk.Label(self.tab1, text="选择包含.tex文件的目录:").pack(anchor=tk.W, padx=10, pady=(20, 5))
        
        frame1 = ttk.Frame(self.tab1)
        frame1.pack(fill=tk.X, padx=10, pady=5)
        
        self.dir_path_var = tk.StringVar()
        ttk.Entry(frame1, textvariable=self.dir_path_var, width=60).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(frame1, text="浏览...", command=self.browse_directory).pack(side=tk.LEFT)
        
        # Reference file selection
        ttk.Label(self.tab1, text="选择参考文献文件 (reference.tex):").pack(anchor=tk.W, padx=10, pady=(20, 5))
        
        frame2 = ttk.Frame(self.tab1)
        frame2.pack(fill=tk.X, padx=10, pady=5)
        
        self.ref_path_var = tk.StringVar()
        ttk.Entry(frame2, textvariable=self.ref_path_var, width=60).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(frame2, text="浏览...", command=self.browse_reference_file).pack(side=tk.LEFT)
        
        # Process button
        ttk.Button(self.tab1, text="处理文件", command=self.process_files).pack(padx=10, pady=30)
        
        # Status information
        frame3 = ttk.LabelFrame(self.tab1, text="状态")
        frame3.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.status_text = tk.Text(frame3, height=10, wrap=tk.WORD)
        self.status_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Scroll bar for status text
        scrollbar = ttk.Scrollbar(self.status_text, command=self.status_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.status_text.config(yscrollcommand=scrollbar.set)
    
    def setup_tab2(self):
        # Frame for missing references
        frame1 = ttk.LabelFrame(self.tab2, text="缺失的引用")
        frame1.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.missing_text = tk.Text(frame1, height=5, wrap=tk.WORD)
        self.missing_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Scroll bar for missing text
        scrollbar1 = ttk.Scrollbar(self.missing_text, command=self.missing_text.yview)
        scrollbar1.pack(side=tk.RIGHT, fill=tk.Y)
        self.missing_text.config(yscrollcommand=scrollbar1.set)
        
        # Frame for citation order
        frame2 = ttk.LabelFrame(self.tab2, text="引用顺序")
        frame2.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.citation_text = tk.Text(frame2, height=10, wrap=tk.WORD)
        self.citation_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Scroll bar for citation text
        scrollbar2 = ttk.Scrollbar(self.citation_text, command=self.citation_text.yview)
        scrollbar2.pack(side=tk.RIGHT, fill=tk.Y)
        self.citation_text.config(yscrollcommand=scrollbar2.set)
        
        # Save options
        frame3 = ttk.Frame(self.tab2)
        frame3.pack(fill=tk.X, padx=10, pady=10)
        
        self.save_option_var = tk.StringVar(value="new")
        ttk.Radiobutton(frame3, text="保存为新文件", variable=self.save_option_var, value="new").pack(side=tk.LEFT, padx=10)
        ttk.Radiobutton(frame3, text="覆盖原文件", variable=self.save_option_var, value="overwrite").pack(side=tk.LEFT, padx=10)
        
        # Save button
        ttk.Button(self.tab2, text="保存参考文献", command=self.save_references).pack(padx=10, pady=10)
    
    def browse_directory(self):
        directory = filedialog.askdirectory(title="选择包含.tex文件的目录")
        if directory:
            self.dir_path_var.set(directory)
    
    def browse_reference_file(self):
        file_path = filedialog.askopenfilename(title="选择参考文献文件", filetypes=[("TeX Files", "*.tex"), ("All Files", "*.*")])
        if file_path:
            self.ref_path_var.set(file_path)
    
    def process_files(self):
        # Clear previous status
        self.status_text.delete(1.0, tk.END)
        self.update_status("开始处理文件...")
        
        directory = self.dir_path_var.get()
        ref_file = self.ref_path_var.get()
        
        if not directory or not ref_file:
            messagebox.showerror("错误", "请选择目录和参考文献文件!")
            return
        
        # Extract citation IDs
        self.update_status("从.tex文件中提取引用ID...")
        bib_id_list = self.bib_manager.read_tex_files_in_order(directory)
        self.update_status(f"找到 {len(bib_id_list)} 个唯一的引用ID.")
        
        # Extract bibitem entries
        self.update_status("从参考文献文件中提取bibitem条目...")
        bibitem_list = self.bib_manager.extract_bibitems(ref_file)
        self.update_status(f"从参考文献文件中提取了 {len(bibitem_list)} 个bibitem条目.")
        
        # Check for missing references
        missing_ids = self.bib_manager.check_missing_references()
        if missing_ids:
            self.update_status(f"警告: 找到 {len(missing_ids)} 个缺失的引用!")
            self.missing_text.delete(1.0, tk.END)
            self.missing_text.insert(tk.END, ", ".join(missing_ids))
        else:
            self.update_status("所有引用都找到了相应的bibitem条目.")
            self.missing_text.delete(1.0, tk.END)
            self.missing_text.insert(tk.END, "没有缺失的引用.")
        
        # Display citation order
        self.citation_text.delete(1.0, tk.END)
        self.citation_text.insert(tk.END, "\n".join(bib_id_list))
        
        # Switch to results tab
        self.notebook.select(1)
        self.update_status("处理完成.")
    
    def save_references(self):
        save_option = self.save_option_var.get()
        
        if save_option == "overwrite":
            output_path = self.ref_path_var.get()
            message = "确定要覆盖原文件吗?"
            if not messagebox.askyesno("确认", message):
                return
        else:  # save_option == "new"
            default_dir = os.path.dirname(self.ref_path_var.get())
            output_path = filedialog.asksaveasfilename(
                title="保存为新文件",
                initialdir=default_dir,
                defaultextension=".tex",
                filetypes=[("TeX Files", "*.tex"), ("All Files", "*.*")]
            )
            if not output_path:
                return
        
        # Save the reordered references
        success = self.bib_manager.reorder_and_save_bibitems(output_path)
        
        if success:
            messagebox.showinfo("成功", f"参考文献已成功保存至 {output_path}")
        else:
            messagebox.showerror("错误", "保存参考文献时出错!")
    
    def update_status(self, message):
        self.status_text.insert(tk.END, message + "\n")
        self.status_text.see(tk.END)
        self.root.update_idletasks()


def main():
    root = tk.Tk()
    app = BibManagerGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()
